﻿#include "MailManager.h"
#include "CenterService.h"
#include "Cache/OfflineCharacterDataMgr.h"
#include "Cache/OfflineCharacterData.h"
#include "Active/ActiveCharacter.h"
#include "Active/ActiveCharacterMgr.h"
#include "Common/Utility/ServerTime.h"
#include "base/core/Utf8AndWideChar.h"
#include "RelationMgr/RelationMgr.h"
#include "AccountMgr/CenterAccountMgr.h"
#include "Common/TableData/FunctionunlockFunctionUnlockCfg.h"

MailManager::MailManager()
{
	_mailMap.clear();
	_extractInfo.clear();
	m_nStartLoadTime = 0;
	m_bFinishLoad = false;
	m_lastSaveTime = 0;
	_mailPool = new ObjectPool < Mail > ;
}

MailManager::~MailManager()
{
	_extractInfo.clear();

	MailIter iter = _mailMap.begin();
	while (iter != _mailMap.end()){
		if (nullptr != iter->second)
		{
			_mailPool->FreeObj(iter->second);
			iter->second = nullptr;
		}
		_mailMap.erase(iter++);
	}

	MMO_DELETE(_mailPool);
}

bool MailManager::Init()
{
	bool result = false;
	bool ret = false;

	IMessageDispatch *pMessageDispatch = g_GetCenterService()->GetServiceMessageDispatch();
	if (nullptr == pMessageDispatch)
	{
		MMOLOG_FMT_ERROR("[center] MailManager::Init....nullptr == pMessageDispatch ");
		return false;
	}
	pMessageDispatch->registerDBProxyHandler(EMODULE_ID_MAIL, this);
	pMessageDispatch->registerClientHandler(EMODULE_ID_MAIL, this);
	pMessageDispatch->registerServerHandler(EMODULE_ID_MAIL, this);

	g_GetActiveCharacterMgr()->AddCharacterObserver(this);

	//start load all trading mail info
	ret = g_GetTimerAxis()->SetTimer(TIMER_ID_StartLoad_MailData, 2 * 1000, this);
	MMOLOG_PROCESS_ERROR(ret);

	result = true;

Exit0:
	return result;
}

bool MailManager::UnInit()
{
	IMessageDispatch *pMessageDispatch = g_GetCenterService()->GetServiceMessageDispatch();
	if (nullptr != pMessageDispatch)
	{
		pMessageDispatch->unregisterDBProxyHandler(EMODULE_ID_MAIL);
		pMessageDispatch->unregisterClientHandler(EMODULE_ID_MAIL);
		pMessageDispatch->unregisterServerHandler(EMODULE_ID_MAIL);
	}

	g_GetTimerAxis()->KillAllTimer(this);

	g_GetActiveCharacterMgr()->RemoveCharacterObserver(this);

	return true;
}

bool MailManager::Update(uint64_t tick, bool isDump)
{
	_UNUSE(tick);
	uint64_t currTime = ServerTime::UnixSec();
	if (currTime >= (m_lastSaveTime + INTRAVAL_SAVE_MAILDATA) || isDump)
	{
		MailIter iter = _mailMap.begin();
		for (; iter != _mailMap.end(); ++iter)
		{
			if (nullptr != iter->second)
			{
				iter->second->SaveData();
			}
		}

		m_lastSaveTime = currTime;
	}

	return true;
}

void MailManager::OnTimer(uint32_t nTimerID)
{
	switch (nTimerID)
	{
	case TIMER_ID_StartLoad_MailData: //start load mail info
	{
		if (g_GetCenterService()->IsConnectDBProxy() && g_GetOfflineCharacterDataMgr()->IsFinishLoad())
		{
			MaxMailIDRequest req;
			if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_MAX_MAIL_ID_REQ, &req))
			{
				MMOLOG_FMT_ERROR("[center] LoadMaxMailIdDBRet SendDataToDBProxy failed");
			}
			else
			{
				LogDebugFmtPrint("[center] start load max mail id data....");
			}

			g_GetTimerAxis()->KillTimer(TIMER_ID_StartLoad_MailData, this);
		}
		break;
	}
	case TIMER_ID_ZONEMAIL_SAVE_WAIT_TIME:
		LogErrFmtPrint("save zone mail to db over time curZoneMailId:%d", m_maxAzDbMailId);
		break;

	case TIMER_ID_ZONE_MAIL_ONLINE_HADLE:
		ZoneMailHandle();
		break;
	default:
		break;
	}
}

void MailManager::onCharacterLogin(ActiveCharacter *pOnlineCharacter)
{
	CharIDType charId = pOnlineCharacter->GetCharID();

	//玩家登录时开始加载玩家在线的邮件列表
	//这里只会加载两种邮件，第一种是非交易邮件 第二种是交易邮件但是已经是提取过的了
	if (g_GetCenterService()->IsConnectDBProxy())
	{
		CharacterMailDBRequest req;
		req.set_charid(charId);
		if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_CHARACTER_MAIL_DATA_REQ, &req))
		{
			MMOLOG_FMT_ERROR("[center] CharacterMailDBRequest SendDataToDBProxy failed...charId:%lu ", charId);
		}
		else
		{
			LogDebugFmtPrint("[center] start load character online mail data....charId:%lu ", charId);
		}
	}
	else
	{
		MMOLOG_FMT_ERROR("[center] MailManager::onCharacterLogin....not disconnect db...charId:%lu ", charId);
	}
}

void MailManager::onCharacterLogout(ActiveCharacter *pOnlineCharacter)
{
	CharIDType charId = pOnlineCharacter->GetCharID();
	//
	ClearAllWaitRetMail(1, charId);
	//
	Mail* pMail = FindMail(charId);
	if (nullptr != pMail)
	{
		pMail->OnLogout();
	}
}

void MailManager::onCharacterUpdate(ActiveCharacter *pOnlineCharacter)
{
	_UNUSE(pOnlineCharacter);
}

void MailManager::onCharacterDel(OfflineCharacterData *pOffLineCharacter)
{
	CharIDType charId = pOffLineCharacter->GetCharID();

	//需要先处理要退信的交易邮件
	Mail* pMail = FindMail(charId);
	if (nullptr != pMail)
	{
		// NdrTradingMail 接口里面有删除迭代器，所以这里 trade_mails 不能用引用
		Mail::MailInfoList trade_mails = pMail->GetTradeMailData();
		for (Mail::MailInfoIter mailIterA = trade_mails.begin(); mailIterA != trade_mails.end(); ++mailIterA)
		{
			MailIDType mailId = mailIterA->mailhead().mailid();
			NdrTradingMail(charId, mailId);
		}
	}

	//请求DB服删除该角色的邮件数据
	if (g_GetCenterService()->IsConnectDBProxy())
	{
		DeleteCharacterMail req;
		req.set_charid(charId);
		if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_CHARACTER_DELETE_MAIL_REQ, &req))
		{
			MMOLOG_FMT_ERROR("[center] DeleteCharacterMail SendDataToDBProxy failed...charId:%llu ", charId);
		}
		else
		{
			LogDebugFmtPrint("[center] start to delete character mail data....");
		}
	}

	//回收内存
	if (nullptr != pMail)
	{
		DeleteMail(charId);
	}
}

void MailManager::onDBProxyMessage(uint32_t actionId, RECV_PACKAGE &package)
{
	switch (actionId)
	{
	case DBPROXY_PROTOCOL_OFFLINE_MAIL_DATA_RSP:
	{
		AllOfflineMailDBResponse rsp;
		if (!rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
		{
			MMOLOG_FMT_ERROR("[center] AllOfflineMailDBResponse return but parse failed....offset:%d,", rsp.offset());
			return;
		}
		LoadOfflineMailDataDBRet(rsp);
	}
	break;

	case DBPROXY_PROTOCOL_TRADING_MAIL_DATA_RSP:
	{
		AllTradingMailDBResponse rsp;
		if (!rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
		{
			MMOLOG_FMT_ERROR("[center] AllTradingMailDBResponse return but parse failed....offset:%d,", rsp.offset());
			return;
		}
		LoadTradingMailDataDBRet(rsp);
	}
	break;

	case DBPROXY_PROTOCOL_CHARACTER_MAIL_DATA_RSP:
	{
		CharacterMailDBResponse rsp;
		if (!rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
		{
			MMOLOG_FMT_ERROR("[center] CharacterMailDBResponse return but parse failed");
			return;
		}

		LoadOnlineCharacterMailDataDBRet(rsp);
	}
	break;

	case DBPROXY_PROTOCOL_MAX_MAIL_ID_RSP:
	{
		MaxMailIDResponse rsp;
		if (!rsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
		{
			MMOLOG_FMT_ERROR("[center] MaxMailIDResponse return but parse failed");
			return;
		}
		LoadMaxMailIdDBRet(rsp);
	}
	break;
	case DBPROXY_PROTOCOL_ALL_ZONE_MAIL_SAVE_RSP:
		ZoneMailSaveHandleRsp(package);
		break;
	case DBPROXY_PROTOCOL_ZONE_MAIL_TRANS_RSP:
		TransZoneMailToCharMailRsp(package);
		break;
	default:
		break;
	}
}

void MailManager::onClientMessage(uint32_t actionId, RECV_PACKAGE& package, uint64_t Id /* = 0 */)
{
	UNPACK_MESSAGE_CHAR_ID(package);

	switch (actionId)
	{
	case CLIENT_TO_LOGIC_SENDMAIL:
	{
		OnSendMailReq(charId, package);
	}
	break;
	case CLIENT_TO_CENTER_GETMAILITEMLIST:
	{
		OnGetMailItemListReq(charId, package);
	}
	break;
	case CLIENT_TO_CENTER_ASKMAIL:
	{
		OnAskMailReq(charId, package);
	}
	break;
	case CLIENT_TO_CENTER_DELETEMAIL:
	{
		OnDeleteMailReq(charId, package);
	}
	break;
	//case CLIENT_TO_CENTER_CHECKNAME:
	//{
	//	OnCheckNameReq(charId, package);
	//}
	//break;
	case CLIENT_TO_CENTER_GETMAILATTACHMENT:
	{
		OnGetMailAttachmentReq(charId, package);
	}
	break;
	case CLIENT_TO_CENTER_GETALLMAILATTACHMENT:
	{
		OnGetAllMailAttachmentReq(charId, package);
	}
	break;
	case CLIENT_TO_CENTER_NDRMAIL:
	{
		OnNdrMailReq(charId, package);
	}
	break;
	default:
		break;
	}
}

void MailManager::onServerMessage(uint32_t actionId, RECV_PACKAGE &package)
{
	switch (actionId)
	{
	case CENTER_SERVER_PROTOCOL_SEND_MAIL_REQ:
	{
		OnSendMailReq(package);
	}
	break;
	case CENTER_SERVER_PROTOCOL_UPDATE_MAIL_NOTIFY:
	{
		OnUpdateMailNotify(package);
	}
	break;
	case CENTER_SERVER_PROTOCOL_SEND_SYSTEM_MAIL:
	{
		SendG2WSysMailReq(package);
	}
	break;
	default:
		break;
	}
}

bool MailManager::OnSendMailReq(RECV_PACKAGE &package)
{
	GWSendMailReq sendMailReq;
	WCSendMailRsp sendMailRsp;

	if (!sendMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnSendMailReq GWSendMailReq parse failed...");
		return false;
	}

	CharIDType charId = sendMailReq.sendid();
	CharIDType targetId = sendMailReq.targetid();

	OfflineCharacterData* pSendOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);

	MailInfo mailInfo;
	MailHead* mailHead = mailInfo.mutable_mailhead();
	mailHead->set_type(sendMailReq.mailtype());
	mailHead->set_sendplayerid(charId);
	mailHead->set_sendplayername(pSendOfflineCharacterData == nullptr ? "" : pSendOfflineCharacterData->GetCharName());
	mailHead->set_destplayerid(targetId);
	mailHead->set_topic(sendMailReq.title().length() <= MAX_MAIL_TOPIC_LEN ? sendMailReq.title() : sendMailReq.title().substr(0,MAX_MAIL_TOPIC_LEN));
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(sendMailReq.attachment().iteminfo_size());

	mailInfo.set_content(sendMailReq.content().length() <= MAX_MAIL_CONTENT_LEN ? sendMailReq.content() : sendMailReq.content().substr(0, MAX_MAIL_CONTENT_LEN));
	mailInfo.set_selltype((uint32_t)sendMailReq.selltype());
	mailInfo.set_sellnum(sendMailReq.sellnum() > 0 ? sendMailReq.sellnum() : 0);
	if (sendMailReq.has_attachment())
	{
		MailAttachmentList *protoAttachList = mailInfo.mutable_attachmentlist();
		protoAttachList->CopyFrom(sendMailReq.attachment());
	}

	ERetCode ret = SendMail(mailInfo);
	if (RET_SUCCESS != ret)
	{
		string strlog = "";
		if (mailInfo.has_attachmentlist())
		{			
			int32_t isize = mailInfo.attachmentlist().iteminfo_size();
			for (int32_t i = 0; i < isize; ++i)
			{
				const ItemProtoInfo &proto = mailInfo.attachmentlist().iteminfo(i);
				char szTmp[50] = { '\0' };
				snprintf(szTmp, sizeof(szTmp) - 1, " id:%u,num:%d,", proto.item_id(), proto.item_num());
				strlog += szTmp;
			}
		}
		
		LogErrFmtPrint("OnSendMailReqxxx...ret:%d,cid:%lu,dstcid:%lu,type:%d,topic:%s,content:%s,attach:%s ", ret, charId, targetId, mailInfo.mailhead().type(), mailInfo.mailhead().topic().c_str(), mailInfo.content().c_str(), strlog.c_str());
	}
	sendMailRsp.set_ret(ret);
	g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
	return true;
}

bool MailManager::OnUpdateMailNotify(RECV_PACKAGE &package)
{
	GWUpdateMailNotify updateMailNotify;
	if (!updateMailNotify.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnUpdateMailNotify GWUpdateMailNotify parse failed...");
		return false;
	}
	return OnUpdateMailNotify(updateMailNotify);
}

bool MailManager::SendG2WSysMailReq(RECV_PACKAGE &package)
{
	GWSendSystemMailReq sendSystemMailReq;

	if (!sendSystemMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::SendG2WSysMailReq GWSendSystemMailReq parse failed...");
		return false;
	}

	CharIDType charId = sendSystemMailReq.destid();
	OfflineCharacterData *pOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
	if (nullptr == pOfflineCharacterData)
	{
		MMOLOG_FMT_ERROR("[center] MailManager::SendG2WSysMailReq but OfflineCharacterData is nullptr,charID:%llu", charId);
		return false;
	}
	//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
	if (nullptr != pRecvOpenLevel)
	{
		if (pOfflineCharacterData->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			LogErrFmtPrint("[center] MailManager::SendG2WSysMailReq...recv mail level not enough...charId:%lu,content:%s,strTopic:%s", charId, sendSystemMailReq.content().c_str(), sendSystemMailReq.title().c_str());
			return false;
		}
	}

	MailInfo mailInfo;
	MailHead* pMailHead = mailInfo.mutable_mailhead();
	pMailHead->set_type(MAIL_TYPE_SYSTEM);
	pMailHead->set_sendplayerid(0);

	string strName = "系统";
	pMailHead->set_sendplayername(strName);
	pMailHead->set_destplayerid(charId);
	pMailHead->set_topic(sendSystemMailReq.title());
	pMailHead->set_createtime(Time::Now().sec());
	pMailHead->set_status(MS_NOT_READ);
	pMailHead->set_attachmentnum(sendSystemMailReq.attachment().iteminfo_size());
	mailInfo.set_gold(sendSystemMailReq.gold());
	mailInfo.set_bindgold(sendSystemMailReq.bindgold());
	mailInfo.set_diamond(sendSystemMailReq.diamond());
	mailInfo.set_binddiamond(sendSystemMailReq.binddiamond());
	mailInfo.set_arenascore(sendSystemMailReq.arenascore());
	mailInfo.set_content(sendSystemMailReq.content());

	if (sendSystemMailReq.has_attachment())
	{
		mailInfo.mutable_attachmentlist()->CopyFrom(sendSystemMailReq.attachment());
	}
	else if (sendSystemMailReq.itemlst_size() > 0)
	{
		LIST_ITEM lstItem;
		lstItem.clear();
		int32_t isize = sendSystemMailReq.itemlst_size();
		for (int32_t i = 0; i < isize; ++i)
		{
			const MailItemProto &proto = sendSystemMailReq.itemlst(i);
			SItem item;
			item.nItemID = proto.itemid();
			item.nNum = proto.num();
			item.byBind = proto.bind();
			lstItem.push_back(item);
		}
		SItemCond itemCond;
		VEC_ITEM_PROTO_EX vecProtoEx;
		vecProtoEx.clear();
		g_GetItemMgr()->CreateItem(lstItem, vecProtoEx, itemCond);
		MailAttachmentList* pMailAttachmentList = mailInfo.mutable_attachmentlist();
		if (nullptr == pMailAttachmentList)
		{
			LogErrFmtPrint("[center] MailManager::SendG2WSysMailReq...nullptr == pMailAttachmentList...dstCharId:%lu,strTopic:%s", charId, sendSystemMailReq.title().c_str());
			return false;
		}
		VEC_ITEM_PROTO_EX::iterator iterItem = vecProtoEx.begin();
		for (; iterItem != vecProtoEx.end(); ++iterItem)
		{
			ItemProtoInfo *protoMailItem = pMailAttachmentList->add_iteminfo();
			if (nullptr != protoMailItem)
			{
				protoMailItem->CopyFrom((*iterItem));
			}
		}
	}

	int32_t ret = SendMail(mailInfo);
	if (RET_SUCCESS != ret)
	{
		string strlog = "";
		if (mailInfo.has_attachmentlist())
		{
			int32_t isize = mailInfo.attachmentlist().iteminfo_size();
			for (int32_t i = 0; i < isize; ++i)
			{
				const ItemProtoInfo &proto = mailInfo.attachmentlist().iteminfo(i);
				char szTmp[50] = { '\0' };
				snprintf(szTmp, sizeof(szTmp) - 1, " id:%u,num:%d,", proto.item_id(), proto.item_num());
				strlog += szTmp;
			}
		}
		LogErrFmtPrint("SendG2WSysMailReq...ret:%d,cid:%lu,type:%d,topic:%s,content:%s,attach:%s ", ret, charId, mailInfo.mailhead().type(), mailInfo.mailhead().topic().c_str(), mailInfo.content().c_str(), strlog.c_str());
		return false;
	}

	return true;
}

void MailManager::LogicCrash(uint32_t logicId)
{
	ClearAllWaitRetMail(2, (uint32_t)logicId);
}
//是否有附件
bool MailManager::HasAttach(const MailInfo& mailInfo)
{
	int32_t hasAttach = 0; //是否有附件
	if (mailInfo.attachmentlist().iteminfo_size() > 0
		|| mailInfo.gold() > 0
		|| mailInfo.bindgold() > 0
		|| mailInfo.diamond() > 0
		|| mailInfo.binddiamond() > 0
		|| mailInfo.arenascore() > 0
		)
	{
		hasAttach = 1;
	}
	return hasAttach;
}

ERetCode MailManager::SendMail(MailInfo& mailInfo, bool notifyPlayer, bool sendOfflineMail)
{
	MailHead* mailHead = mailInfo.mutable_mailhead();
	if (nullptr == mailHead)
	{
		LogErrFmtPrint("MailManager::SendMail...nullptr == mailHead...");
		return RET_PARMAR_ERROR;
	}
	CharIDType fromPlayerId = mailHead->sendplayerid();
	CharIDType destPlayerId = mailHead->destplayerid();

	Mail* pTargetMail = MutMail(destPlayerId);
	if (nullptr == pTargetMail)
	{
		return RET_MAIL_DATA_ERROR;
	}
	if (mailHead->topic().length() <= 0)
	{
		LogErrFmtPrint("MailManager::SendMail...mailHead->topic().length() <= 0...fromPlayerId:%lu, destPlayerId:%lu ", fromPlayerId, destPlayerId);
		return RET_MAIL_TOPIC_LEN_LIMIT;
	}
	
	if (mailInfo.has_attachmentlist())
	{
		int32_t attachSize = mailInfo.attachmentlist().iteminfo_size();
		if (attachSize > MAX_MAIL_ATTATCH_NUM)
		{
			LogErrFmtPrint("MailManager::SendMail...attachSize > MAX_MAIL_ATTATCH_NUM...fromPlayerId:%lu, destPlayerId:%lu,attachSize:%d ", fromPlayerId, destPlayerId,attachSize);
			return RET_MAIL_ATTATCH_COUNT_LIMIT;
		}
	}

	ActiveCharacter* pTargetActiveCharacter = g_GetActiveCharacterMgr()->GetOnlineCharacter(destPlayerId);
	//检查交易邮件
	if (mailHead->type() == MAIL_TYPE_TRADE)
	{
		//如果对方不在线那么无法发送交易邮件
		if (nullptr == pTargetActiveCharacter)
		{
			MMOLOG_FMT_ERROR("MailManager::SendMail fromPlayerId=%llu send mail to destPlayerId=%llu, but pTargetActiveCharacter is nullptr", fromPlayerId, destPlayerId);
			return RET_MAIL_TRADE_NOT_SEND_OFFLINE;
		}

		//如果对方在线并且对方在线数据还未加载完毕那么无法发送交易邮件
		if (!pTargetMail->IsLogin())
		{
			MMOLOG_FMT_ERROR("MailManager::SendMail fromPlayerId=%llu send mail to destPlayerId=%llu, but pTargetMail online mail data no already", fromPlayerId, destPlayerId);
			return RET_MAIL_TRADE_NOT_SEND_OFFLINE;
		}
	}

	//检查附件
	if (mailHead->attachmentnum() != (uint32_t)mailInfo.attachmentlist().iteminfo_size())
	{
		mailHead->set_attachmentnum(mailInfo.attachmentlist().iteminfo_size());
	}

	int32_t hasAttach = HasAttach(mailInfo); //是否有附件
	//初始化邮件ID
	mailHead->set_mailid(GetNewMailId());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_has_attach(hasAttach);
	//主题和内容
	string strTopic = mailHead->topic();
	string strContent = mailInfo.content();
	mailHead->set_topic(strTopic.length() <= MAX_MAIL_TOPIC_LEN ? strTopic : strTopic.substr(0, MAX_MAIL_TOPIC_LEN));
	mailInfo.set_content(strContent.length() <= MAX_MAIL_CONTENT_LEN ? strContent : strContent.substr(0, MAX_MAIL_CONTENT_LEN));
	//删除的日志
	string strlog = "";
	//检查是否要删除邮件
	Mail::MailInfoList& mails = pTargetMail->GetMailData();
	Mail::MailInfoList& tarde_mails = pTargetMail->GetTradeMailData();
	if (pTargetMail->GetAllNum() >= MAX_MAIL)
	{
		MailInfo *pDelMailInfo = pTargetMail->FindCanDel();
		if (nullptr != pDelMailInfo)
		{
			bool bDel = false;
			uint32_t delMailId = pDelMailInfo->mailhead().mailid();
			if (MAIL_TYPE_TRADE == pDelMailInfo->mailhead().type())
			{
				//交易邮件
				Mail::MailInfoIter iter = tarde_mails.begin();
				for (; iter != tarde_mails.end(); ++iter)
				{
					if (iter->mailhead().mailid() == delMailId)
					{
						pTargetMail->CancelTradeMailTimer(delMailId);
						tarde_mails.erase(iter);
						bDel = true;
						char szTmp[100] = { '\0' };
						snprintf(szTmp,sizeof(szTmp)-1, "trade:%u ", delMailId);
						strlog += szTmp;
						break;
					}
				}
			}
			else
			{
				//非交易邮件
				Mail::MailInfoIter iter = mails.begin();
				for (; iter != mails.end(); ++iter)
				{
					if (iter->mailhead().mailid() == delMailId)
					{
						mails.erase(iter);
						bDel = true;
						char szTmp[100] = { '\0' };
						snprintf(szTmp, sizeof(szTmp) - 1, "normal:%u ", delMailId);
						strlog += szTmp;
						break;
					}
				}
			}
			if (bDel)
			{
				pTargetMail->CacheUpdateMailId(delMailId, UNIT_DELETE);
				if (notifyPlayer == true && nullptr != pTargetActiveCharacter)
				{
					WCDeleteMailRsp delMsg;
					delMsg.add_idlist(delMailId);
					g_GetCenterService()->SendDataToClient(destPlayerId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_DELETEMAIL, &delMsg);
				}
			}
		}
	}

	//如果是交易邮件，那么将交易邮件放到到交易邮件列表中并且开启定时器
	if (mailHead->type() == MAIL_TYPE_TRADE)
	{
		tarde_mails.push_back(mailInfo);
		pTargetMail->StartTradeMailTimer(mailHead->mailid());
		pTargetMail->CacheUpdateMailId(mailInfo.mailhead().mailid(), UNIT_UPDATE);
		
		MMOLOG_FMT_INFO("MailManager::SendMail trade mail cid:%llu ,dst:%llu,mailid:%u,strlog:%s,attachnum:%d,gold:%u,diamond:%u,binddiamond:%u,topic:%s,content:%s ", fromPlayerId, destPlayerId, mailHead->mailid(), strlog.c_str(), mailHead->attachmentnum(), mailInfo.gold(), mailInfo.diamond(), mailInfo.binddiamond(), mailHead->topic().c_str(), mailInfo.content().c_str());

		//将交易邮件发送给对方
		if (notifyPlayer == true)
		{
			WCNewMailNotify notifyMsg;
			MailHead* mailHead = notifyMsg.mutable_mailhead();
			mailHead->CopyFrom(mailInfo.mailhead());
			g_GetCenterService()->SendDataToClient(destPlayerId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_NEWMAILNOTIFY, &notifyMsg);

			//添加交易关系
			g_GetRelationMgr()->UpdateRecentList(fromPlayerId, destPlayerId, RELATION_RECENT_TRADE);
		}
	}
	else
	{
		MMOLOG_FMT_INFO("MailManager::SendMail auction mail cid:%llu ,dst:%llu,mailid:%u,strlog:%s,attachnum:%d,gold:%u,diamond:%u,binddiamond:%u,topic:%s,content:%s ", fromPlayerId, destPlayerId, mailHead->mailid(), strlog.c_str(), mailHead->attachmentnum(), mailInfo.gold(), mailInfo.diamond(), mailInfo.binddiamond(), mailHead->topic().c_str(), mailInfo.content().c_str());
		//如果对方在线但是在线邮件数据还未加载完毕那么统一当成离线进行处理
		if (nullptr != pTargetActiveCharacter && pTargetMail->IsLogin())
		{
			mails.push_back(mailInfo);
			pTargetMail->CacheUpdateMailId(mailInfo.mailhead().mailid(), UNIT_UPDATE);
			if (notifyPlayer == true)
			{
				WCNewMailNotify notifyMsg;
				MailHead* mailHead = notifyMsg.mutable_mailhead();
				mailHead->CopyFrom(mailInfo.mailhead());
				g_GetCenterService()->SendDataToClient(destPlayerId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_NEWMAILNOTIFY, &notifyMsg);
			}			
		}
		else if (sendOfflineMail) //这种情况下放到到离线列表中
		{
			pTargetMail->CacheOfflineMail(mailInfo);
		}
		else
		{
			bool onlineFlag = (nullptr != pTargetActiveCharacter) ? true : false;
			bool loginFlag = pTargetMail->IsLogin();
			LogErrFmtPrint("[center] MailManager::SendMailxxx...fromPlayerId=%lu send mail to destPlayerId=%lu,onlineFlag:%d,loginFlag:%d ", fromPlayerId, destPlayerId, (int32_t)onlineFlag, (int32_t)loginFlag);
			return RET_FAIL;
		}
	}

	return RET_SUCCESS;
}

ERetCode MailManager::ValidateMail(CharIDType fromPlayerId, CharIDType targetPlayerId)
{
	//check black
	_UNUSE(fromPlayerId);
	_UNUSE(targetPlayerId);
	return RET_SUCCESS;
}

ERetCode MailManager::GetMailAttachment(CharIDType charId, MailIDType mailId)
{
	Mail* pMail = FindMail(charId);
	//邮件数据还没准备好不给提取附件
	if (pMail == nullptr || !pMail->IsLogin())
	{
		MMOLOG_FMT_ERROR("MailManager::GetMailAttachment, pMail is null or pMail not login, charId:%llu, mailId:%u", charId, mailId);
		return RET_FAIL;
	}

	PlayerExtractStateMap::iterator exIter = _extractInfo.find(charId);
	if (exIter != _extractInfo.end())
	{
		MMOLOG_FMT_ERROR("MailManager::GetMailAttachment, player is extractIng attachment, charId:%llu, mailId:%u", charId, mailId);
		return RET_FAIL;
	}

	if (nullptr != pMail)
	{
		uint32_t logicId = g_GetCenterAccountMgr()->GetLogicIdByCid(charId);
		bool bFind = false;
		MailInfo *pMailInfo = nullptr;
		Mail::MailInfoList& mails = pMail->GetMailData();
		Mail::MailInfoList& trade_mails = pMail->GetTradeMailData();
		for (Mail::MailInfoIter iter = mails.begin(); iter != mails.end(); ++iter)
		{
			MailInfo& mailInfo = (*iter);
			const MailHead& mailHead = mailInfo.mailhead();
			if (mailHead.mailid() == mailId)
			{
				pMailInfo = &mailInfo;
				bFind = true;
			}
		}
		if (!bFind)
		{
			for (Mail::MailInfoIter iterA = trade_mails.begin(); iterA != trade_mails.end(); ++iterA)
			{
				MailInfo& mailInfo = (*iterA);
				const MailHead& mailHead = mailInfo.mailhead();
				if (mailHead.mailid() == mailId)
				{
					pMailInfo = &mailInfo;
					bFind = true;
				}
			}
		}

		if (!bFind || nullptr == pMailInfo)
		{
			return RET_MAIL_NOT_EXIST;
		}
		else if (pMailInfo->mailhead().fetch())
		{
			return RET_MAIL_ALREADY_FETCH;
		}
		else if (pMailInfo->gold() > 0 || pMailInfo->bindgold() || pMailInfo->diamond() ||
			pMailInfo->binddiamond() > 0 || pMailInfo->arenascore() > 0 || pMailInfo->mailhead().attachmentnum() > 0)
		{
			const MailHead& mailHead = pMailInfo->mailhead();

			MailExtractStateMap extractMap;
			MailExtractState extractState;
			//设置提取标识
			extractState.getFlag = true;
			extractState.logicId = logicId;
			//保存玩家提取标识
			extractMap[mailId] = extractState;

			//向逻辑服发送提取邮件附件
			WGGetMailAttachmentReq getMailAttachmentMsg;
			getMailAttachmentMsg.set_charid(charId);
			getMailAttachmentMsg.set_mailid(mailId);
			getMailAttachmentMsg.set_mailtype(mailHead.type());
			getMailAttachmentMsg.set_gold(pMailInfo->gold());
			getMailAttachmentMsg.set_bindgold(pMailInfo->bindgold());
			getMailAttachmentMsg.set_diamond(pMailInfo->diamond());
			getMailAttachmentMsg.set_binddiamond(pMailInfo->binddiamond());
			getMailAttachmentMsg.set_arenascore(pMailInfo->arenascore());
			getMailAttachmentMsg.set_selltype(pMailInfo->selltype());
			getMailAttachmentMsg.set_sellnum(pMailInfo->sellnum());
			getMailAttachmentMsg.mutable_attachment()->CopyFrom(pMailInfo->attachmentlist());
			getMailAttachmentMsg.set_source(pMailInfo->source());
			if (!g_GetCenterService()->SendDataToLogic(logicId, EMODULE_ID_MAIL, CENTER_SERVER_PROTOCOL_GET_MAIL_ATTACHMENT_TO_LOGIC, &getMailAttachmentMsg))
			{
				MMOLOG_FMT_ERROR("MailManager::GetMailAttachment....SendDataToLogic failed...charid:%lu,logicid:%u  ",charId,logicId);
				return RET_FAIL;
			}
			//
			_extractInfo[charId] = extractMap;
			//如果是交易邮件那么在这个时候取消掉定时器
			if (mailHead.type() == MAIL_TYPE_TRADE)
			{
				pMail->CancelTradeMailTimer(mailId);
			}

			return RET_SUCCESS;
		}
		else
		{
			return RET_MAIL_NOT_HAVE_ATTACHMENT;
		}
	}
	else
	{
		MMOLOG_FMT_ERROR("MailManager::GetMailAttachment, but pMail is nullptr, charId:%llu", charId);
	}

	return RET_FAIL;
}

ERetCode MailManager::GetAllMailAttachment(CharIDType charId)
{
	PlayerExtractStateMap::iterator exIter = _extractInfo.find(charId);
	if (exIter != _extractInfo.end())
	{
		MMOLOG_FMT_ERROR("MailManager::GetAllMailAttachment, player is extractIng attachment, charId:%llu", charId);
		return RET_FAIL;
	}

	Mail* pMail = FindMail(charId);
	if (nullptr == pMail || !pMail->IsLogin())
	{
		MMOLOG_FMT_ERROR("MailManager::GetAllMailAttachment, but pMail is nullptr or pMail is not login, charId:%llu", charId);
		return RET_FAIL;
	}

	WGGetAllMailAttachmentReq getAllMailAttachmentMsg;
	MailExtractStateMap extractMap;
	extractMap.clear();
	uint32_t logicId = g_GetCenterAccountMgr()->GetLogicIdByCid(charId);

	ERetCode retCode = RET_SUCCESS;
	if (pMail->GetTradeMailData().size() > 0)
		retCode = RET_MAIL_NO_MAIL_TO_FETCH;

	Mail::MailInfoList& mails = pMail->GetMailData();
	for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end(); ++mailIter)
	{
		MailInfo &mailInfo = (*mailIter);
		if (mailInfo.mailhead().type() == MAIL_TYPE_NORMAL || mailInfo.mailhead().type() == MAIL_TYPE_TRADE) //交易邮件无法一键提取
		{
			continue;
		}
		if (mailInfo.mailhead().fetch())
		{
			continue;
		}
		const MailHead &mailHead = mailInfo.mailhead();
		if (mailInfo.gold() > 0 || mailInfo.bindgold() > 0 || mailInfo.diamond() > 0 ||
			mailInfo.binddiamond() > 0 || mailInfo.arenascore() || mailInfo.mailhead().attachmentnum() > 0
			)
		{
			OneMailAttachmentData* pAttachmentData = getAllMailAttachmentMsg.add_attachmentdatalist();
			if (nullptr != pAttachmentData)
			{
				pAttachmentData->set_mailid(mailHead.mailid());
				pAttachmentData->set_gold(mailInfo.gold());
				pAttachmentData->set_bindgold(mailInfo.bindgold());
				pAttachmentData->set_diamond(mailInfo.diamond());
				pAttachmentData->set_binddiamond(mailInfo.binddiamond());
				pAttachmentData->set_arenascore(mailInfo.arenascore());
				pAttachmentData->mutable_attachment()->CopyFrom(mailIter->attachmentlist());
				pAttachmentData->set_source(mailInfo.source());
				MailExtractState extractState;
				extractState.getFlag = true; //设置提取状态
				extractState.logicId = logicId;
				extractMap[mailHead.mailid()] = extractState;
			}		
		}
	}
	if (getAllMailAttachmentMsg.attachmentdatalist_size() > 0)
	{
		getAllMailAttachmentMsg.set_charid(charId);
		if (!g_GetCenterService()->SendDataToLogic(logicId, EMODULE_ID_MAIL, CENTER_SERVER_PROTOCOL_GET_ALL_MAIL_ATTACHMENT_TO_LOGIC, &getAllMailAttachmentMsg))
		{
			MMOLOG_FMT_ERROR("MailManager::GetAllMailAttachment...SendDataToLogic failed...charid:%lu,logicId:%u ", charId, logicId);
			return RET_FAIL;
		}
		_extractInfo[charId] = extractMap;
		retCode = RET_SUCCESS;
	}
	return retCode;
}

bool MailManager::OnUpdateMailNotify(GWUpdateMailNotify updateMailNotify)
{
	CharIDType charId = updateMailNotify.charid();
	//清除提取标记
	PlayerExtractStateMap::iterator exIter = _extractInfo.find(charId);
	if (exIter != _extractInfo.end())
	{
		_extractInfo.erase(exIter);
	}
	else
	{
		MMOLOG_FMT_ERROR("MailManager::OnUpdateMailNotify11.... exIter = end(), charid:%llu ", charId);
	}

	Mail* pMail = MutMail(charId);
	if (nullptr == pMail)
	{
		MMOLOG_FMT_ERROR("MailManager::OnUpdateMailNotify11 but pMail is nullptr,charId:%llu", charId);
		return false;
	}

	int32_t delsize = updateMailNotify.idlist_size();
	Mail::MailInfoList& mails = pMail->GetMailData();
	Mail::MailInfoList& trade_mails = pMail->GetTradeMailData();
	for (int32_t i = 0; i < delsize; ++i)
	{
		bool bFind = false;
		MailIDType fetchMailId = updateMailNotify.idlist(i);
		MailInfo *pMailInfo = nullptr;
		for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end(); ++mailIter)
		{
			MailIDType mailId = mailIter->mailhead().mailid();
			if (mailId == fetchMailId)
			{
				pMailInfo = &(*mailIter);
				bFind = true;
				break;
			}
		}
		Mail::MailInfoIter mailIterDel = trade_mails.end();
		if (!bFind)
		{
			for (Mail::MailInfoIter mailIterA = trade_mails.begin(); mailIterA != trade_mails.end(); ++mailIterA)
			{
				MailIDType mailId = mailIterA->mailhead().mailid();
				if (mailId == fetchMailId)
				{
					mailIterDel = mailIterA;
					pMailInfo = &(*mailIterA);
					bFind = true;
					break;
				}
			}
		}
		if (nullptr == pMailInfo)
		{
			continue;
		}
		MailHead* mailHead = pMailInfo->mutable_mailhead();
		if (RET_SUCCESS == updateMailNotify.ret())
		{
			int32_t sellType = pMailInfo->selltype();
			int32_t sellNum = pMailInfo->sellnum();
			CharIDType destChar = mailHead->sendplayerid();

			//
			LogInfoFmtPrint("OnUpdateMailNotify11..fetchMailId:%u,charId:%lu,islogin:%d, dstchar:%lu,sellType:%d,sellNum:%d,type:%d,topic:%s,content:%s ", fetchMailId, charId, (int32_t)pMail->IsLogin(), destChar, sellType, sellNum, mailHead->type(), mailHead->topic().c_str(), pMailInfo->content().c_str());

			//设置已读
			mailHead->set_status(MS_READED);
			//设置已领取
			mailHead->set_fetch(1);
			//如果是交易邮件
			if (mailHead->type() == MAIL_TYPE_TRADE)
			{
				//移动到在线列表中
				if (mailIterDel != trade_mails.end())
				{
					ActiveCharacter* pActiveCharacter = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
					//如果玩家在线那么将邮件放到在线列表中去
					if (nullptr != pActiveCharacter && pMail->IsLogin())
					{
						mails.push_back(*pMailInfo);
						pMail->CacheUpdateMailId(fetchMailId, UNIT_UPDATE);
					}
					//不在线的话将邮件放到离线列表中去
					else
					{
						pMail->CacheOfflineMail(*pMailInfo);
					}

					//移除交易邮件列表记录
					trade_mails.erase(mailIterDel);
				}
				else
				{
					LogErrFmtPrint("[center] MailManager::OnUpdateMailNotify11...fetch mail is trade mail ,but not in trade_mails...charid:%llu, mailid:%u ", charId, fetchMailId);
				}

				//交易成功，发送交易收益邮件,这里查询下目标玩家
				OfflineCharacterData *pSrcOffline = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
				if (nullptr != pSrcOffline)
				{
					MailInfo protomail;
					MailHead *protHead = protomail.mutable_mailhead();
					protHead->set_type(MAIL_TYPE_PAY); //已付款
					protHead->set_destplayerid(destChar);

					protHead->set_sendplayerid(charId);
					protHead->set_sendplayername(pSrcOffline->GetCharName());
					protHead->set_status(MS_NOT_READ);
					string strTopic = "交易收益";
					protHead->set_topic(strTopic);
					string strContent = "你有一笔邮件交易已成功,系统自动扣除5%的交易税,请尽快收取交易利润。";
					protomail.set_content(strContent);
					//收益是向上取整的
					float fsellnum = (float)sellNum;
					sellNum = (int32_t)ceil(fsellnum - (fsellnum * 5.0f / 100.0f));
					if (sellType == MAIL_TRADING_SELL_TYPE_DIAMOND)
					{
						protomail.set_diamond(sellNum);
					}
					else
					{
						protomail.set_gold(sellNum);
					}
					protomail.set_source(MAIL_SOURCE_AUCTIONEER); //交易邮件默认也是拍卖行来源
					//邮件ID,时间 在SendMail中设置的
					int32_t retsnd = SendMail(protomail);
					if (RET_SUCCESS != retsnd)
					{
						LogErrFmtPrint("MailManager::OnUpdateMailNotify11...SendMail failed...charId:%lu,destChar:%lu,sellType:%d,sellNum:%d,content:%s,ret:%d ", charId, destChar, sellType, sellNum, strContent.c_str(),retsnd);
					}
				}
				else
				{
					LogErrFmtPrint("[center] MailManager::OnUpdateMailNotify11...fetch mail is trade mail ,but not find dest Char...charid:%llu, mailid:%u,sellType:%d,sellnum:%d ,destcharid:%u ", charId, fetchMailId, sellType, sellNum, destChar);
				}
			}
			else
			{//非交易邮件
				pMail->CacheUpdateMailId(fetchMailId, UNIT_UPDATE);
			}			
		}
		else
		{
			//如果提取不成功，重新开启交易邮件定时器
			if (mailHead->type() == MAIL_TYPE_TRADE)
			{
				pMail->RestartTradeMailTimer(fetchMailId);
			}
		}
	}
	return true;
}

void MailManager::CleanHadExtractAttachment(CharIDType charId)
{
	PlayerExtractStateMap::iterator exIter = _extractInfo.find(charId);

	if (exIter == _extractInfo.end())
	{
		return;
	}

	Mail* pMail = FindMail(charId);
	if (nullptr == pMail)
	{
		return;
	}

	Mail::MailInfoList& mails = pMail->GetMailData();

	MailExtractStateMap extractStateMap = exIter->second;

	MailExtractStateMap::iterator mIter = extractStateMap.begin();

	for (; mIter != extractStateMap.end(); mIter++)
	{
		for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end(); ++mailIter)
		{
			MailHead* pMailHead = mailIter->mutable_mailhead();
			MailIDType mailId = pMailHead->mailid();

			if (mailId == mIter->first)
			{
				pMailHead->set_fetch(1);//设置已领取

				MailExtractState state = mIter->second;

				if (true == state.getFlag)
				{
					mailIter->set_gold(0);
					mailIter->set_bindgold(0);
					mailIter->set_diamond(0);
					mailIter->set_binddiamond(0);
					mailIter->set_arenascore(0);
					pMailHead->set_attachmentnum(0);
					mailIter->clear_attachmentlist();

					pMail->CacheUpdateMailId(mailId, UNIT_UPDATE);
				}
			}
		}
	}

	_extractInfo.erase(exIter);
}

bool MailManager::OnGetMailAttachmentReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWGetMailAttachmentReq getMailAttachmentReq;
	GCGetMailAttachmentRsp getMailAttachmentRsp;

	if (!getMailAttachmentReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnGetMailAttachmentReq CWGetMailAttachmentReq parse failed...");
		return false;
	}

	//玩家不在线不给提取附件
	ActiveCharacter *pOnlineChar = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnlineChar)
	{
		LogErrFmtPrint("MailManager::OnGetMailAttachmentReq....nullptr == pOnlineChar...charid:%llu ", charId);
		return false;
	}

	MailIDType mailId = getMailAttachmentReq.mailid();

	ERetCode ret = GetMailAttachment(charId, mailId);
	if (ret != RET_SUCCESS)
	{
		getMailAttachmentRsp.set_ret(ret);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_GETMAILATTACHMENT, &getMailAttachmentRsp);
	}

	return true;
}

bool MailManager::OnGetAllMailAttachmentReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWGetAllMailAttachmentReq getAllMailAttachmentReq;
	GCGetAllMailAttachmentRsp getAllMailAttachmentRsp;

	if (!getAllMailAttachmentReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnGetAllMailAttachmentReq CWGetAllMailAttachmentReq parse failed...");
		return false;
	}
	ActiveCharacter *pOnlineChar = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnlineChar)
	{
		LogErrFmtPrint("MailManager::OnGetAllMailAttachmentReq....nullptr == pOnlineChar...charid:%llu ", charId);
		return false;
	}


	ERetCode ret = GetAllMailAttachment(charId);
	if (ret != RET_SUCCESS)
	{
		getAllMailAttachmentRsp.set_ret(ret);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_GETALLMAILATTACHMENT, &getAllMailAttachmentRsp);
	}

	return true;
}

bool MailManager::OnNdrMailReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWNdrMailReq ndrMailReq;
	CWNdrMailRsp ndrMailRsp;

	if (!ndrMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnNdrMailReq CWNdrMailReq parse failed...");
		return false;
	}
	ActiveCharacter *pOnlineChar = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnlineChar)
	{
		LogErrFmtPrint("MailManager::OnNdrMailReq....nullptr == pOnlineChar...charid:%llu ", charId);
		return false;
	}

	MailIDType mailId = ndrMailReq.mailid();
	NdrTradingMail(charId, mailId);

	return true;
}

bool MailManager::OnSendMailReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWSendMailReq sendMailReq;
	WCSendMailRsp sendMailRsp;

	if (!sendMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("[center] MailManager::OnSendMailReq CWSendMailReq parse failed...");
		return false;
	}

	string targetName = sendMailReq.targetname();
	OfflineCharacterData* pOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
	if (nullptr == pOfflineCharacterData)
	{
		MMOLOG_FMT_ERROR("[center] MailManager::OnCheckNameReq but pOfflineCharacterData is nullptr charId:%llu", charId);
		return false;
	}

	string charName = pOfflineCharacterData->GetCharName();
	if (targetName == charName)
	{
		sendMailRsp.set_ret(RET_MAIL_NO_SEND_TO_SELF);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}

	OfflineCharacterData* pTargetOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacterByName(targetName.c_str());
	if (nullptr == pTargetOfflineCharacterData)
	{
		sendMailRsp.set_ret(RET_MAIL_SEND_TARGET_ERROR);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}
	//开启等级 FunctionUnlock_ID_TYPE_SEND_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pSendOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_SEND_MAIL);
	if (nullptr != pSendOpenLevel)
	{
		if (pOfflineCharacterData->GetLevel() < pSendOpenLevel->unlockLevel)
		{
			sendMailRsp.set_ret(RET_LEVEL_LACK);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
	}
	//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
	if (nullptr != pRecvOpenLevel)
	{
		if (pTargetOfflineCharacterData->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			sendMailRsp.set_ret(RET_OTHER_LEVEL_LACK);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}		
	}

	//无法发送系统邮件
	uint32_t mailType = sendMailReq.mailtype();
	if (mailType != MAIL_TYPE_NORMAL && mailType != MAIL_TYPE_TRADE)
	{
		sendMailRsp.set_ret(RET_MAIL_TYPE_ERROR);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}
	int32_t lenContent = sendMailReq.content().length();
	if (lenContent > MAX_MAIL_CONTENT_LEN)
	{
		sendMailRsp.set_ret(RET_MAIL_CONTENT_LEN_LIMIT);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}
	int32_t lenTopic = sendMailReq.title().length();
	if (lenTopic > MAX_MAIL_TOPIC_LEN || lenTopic <= 0)
	{
		sendMailRsp.set_ret(RET_MAIL_TOPIC_LEN_LIMIT);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}
	if (sendMailReq.mailattachment_size() > MAX_MAIL_ATTATCH_NUM)
	{
		sendMailRsp.set_ret(RET_MAIL_ATTATCH_COUNT_LIMIT);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}

	CharIDType destPlayerId = pTargetOfflineCharacterData->GetCharID();
	sendMailReq.set_targetid(destPlayerId);

	Mail* pTargetMail = MutMail(destPlayerId);
	if (nullptr == pTargetMail)
	{
		sendMailRsp.set_ret(RET_MAIL_DATA_ERROR);
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
		return false;
	}

	/*ActiveCharacter* pTargetActiveCharacter = g_GetActiveCharacterMgr()->GetOnlineCharacter(destPlayerId);
	if (sendMailReq.mailtype() == MAIL_TYPE_TRADE) 	//if the trading mail
	{
		//if target is offline so can not sell trading mail
		if (nullptr == pTargetActiveCharacter)
		{
			MMOLOG_FMT_ERROR("MailManager::SendMail fromPlayerId=%llu send mail to destPlayerId=%llu, but pTargetActiveCharacter is nullptr", charId, destPlayerId);
			sendMailRsp.set_ret(RET_MAIL_TRADE_NOT_SEND_OFFLINE);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}

		if (!pTargetMail->IsLogin())
		{
			MMOLOG_FMT_ERROR("MailManager::SendMail fromPlayerId=%llu send mail to destPlayerId=%llu, but pTargetMail online mail data no already", charId, destPlayerId);
			sendMailRsp.set_ret(RET_MAIL_TRADE_NOT_SEND_OFFLINE);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
		if (sendMailReq.selltype() != MAIL_TRADING_SELL_TYPE_GOLD && sendMailReq.selltype() != MAIL_TRADING_SELL_TYPE_DIAMOND)
		{
			//货币类型错误
			sendMailRsp.set_ret(RET_MAIL_SELL_TYPE_ERROR);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
		if (sendMailReq.sellnum() < 1)
		{
			//货币数量错误
			sendMailRsp.set_ret(RET_MAIL_SELL_NUM_ERROR);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
	}
	else*/
	{
		if (sendMailReq.mailtype() != MAIL_TYPE_NORMAL)
		{
			//邮件类型错误
			sendMailRsp.set_ret(RET_MAIL_TYPE_ERROR);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
		//normal mail
		if (sendMailReq.selltype() != 0 )
		{
			//货币类型错误
			sendMailRsp.set_ret(RET_MAIL_SELL_TYPE_ERROR);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
		if (sendMailReq.sellnum() != 0)
		{
			//货币数量错误
			sendMailRsp.set_ret(RET_MAIL_SELL_NUM_ERROR);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_SENDMAIL, &sendMailRsp);
			return false;
		}
	}
	
	WGSendMailReq req;
	req.set_charid(charId);
	req.mutable_req()->CopyFrom(sendMailReq);
	g_GetCenterService()->SendDataToLogicByCharID(charId, EMODULE_ID_MAIL, CENTER_SERVER_PROTOCOL_SEND_MAIL_TO_LOGIC, &req);

	return true;
}

ERetCode MailManager::NdrTradingMail(CharIDType charId, MailIDType mailId)
{
/*
	ActiveCharacter* pActiveCharacter = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pActiveCharacter)
	{
		MMOLOG_FMT_ERROR("MailManager::NdrTradingMail but pActiveCharacter is nullptr,charId:%u", charId);
		return RET_FAIL;
	}*/
	OfflineCharacterData* pCharOffline = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(charId);
	if (nullptr == pCharOffline)
	{
		MMOLOG_FMT_ERROR("MailManager::NdrTradingMail but OfflineCharacterData is nullptr,charId:%lu,mailid:%u ", charId,mailId);
		return RET_FAIL;
	}

	Mail* pMail = FindMail(charId);
	if (nullptr == pMail)
	{
		MMOLOG_FMT_ERROR("MailManager::NdrTradingMail but pMail is nullptr,charId:%lu,mailid:%u", charId,mailId);
		return RET_FAIL;
	}

	Mail::MailInfoList& tradeMails = pMail->GetTradeMailData();
	for (Mail::MailInfoIter iter = tradeMails.begin(); iter != tradeMails.end(); ++iter)
	{
		MailIDType tradeMailId = iter->mailhead().mailid();
		if (tradeMailId == mailId)
		{
			pMail->CancelTradeMailTimer(mailId);
			//send mail to destId
			CharIDType destId = iter->mailhead().sendplayerid();
			OfflineCharacterData* pDestOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(destId);
			if (nullptr != pDestOfflineCharacterData)
			{
				MailInfo mailInfo;
				mailInfo.CopyFrom((*iter));
				MailHead* pMailHead = mailInfo.mutable_mailhead();
				pMailHead->set_mailid(GetNewMailId());
				pMailHead->set_type(MAIL_TYPE_RETURN);
				pMailHead->set_sendplayerid(charId);
				pMailHead->set_sendplayername(pCharOffline->GetCharName());
				pMailHead->set_destplayerid(destId);
				string strTopic = "交易邮件退信";
				pMailHead->set_topic(strTopic);
				pMailHead->set_createtime(Time::Now().sec());
				pMailHead->set_status(MS_NOT_READ);
				pMailHead->set_fetch(0);
				string strContent = "您有一笔交易邮件已失败，请尽快收取附件。";
				mailInfo.set_content(strContent);
				//send mail
				ERetCode ret = SendMail(mailInfo);
				if (RET_SUCCESS != ret)
				{
					MMOLOG_FMT_ERROR("[center] MailManager::NdrTradingMail , SendMail failed....charid:%lu, mailid:%u,destcharid:%lu,ret:%d,strTopic:%s ", charId, mailId, destId, ret, strTopic.c_str());
				}
			}
			else
			{
				MMOLOG_FMT_ERROR("[center] MailManager::NdrTradingMail , can not find dest char....charid:%lu, mailid:%u,destcharid:%lu ", charId, mailId, destId);
			}
			//delete trade mail
			tradeMails.erase(iter);
			pMail->CacheUpdateMailId(mailId, UNIT_DELETE);
			//notify
			ActiveCharacter* pActiveCharacter = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
			if (nullptr != pActiveCharacter)
			{
				WCDeleteMailRsp deleteMailRsp;
				deleteMailRsp.add_idlist(mailId);
				g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_DELETEMAIL, &deleteMailRsp);
			}				

			return RET_SUCCESS;
		}
	}

	return RET_FAIL;
}

bool MailManager::DeleteMail(CharIDType charId, std::vector<MailIDType>& mailIdList, bool ignoreAttachment)
{
	WCDeleteMailRsp deleteMailRsp;

	Mail* pMail = FindMail(charId);
	if (nullptr == pMail)
	{
		g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_DELETEMAIL, &deleteMailRsp);
		return false;
	}

	Mail::MailInfoList&	mails = pMail->GetMailData();

	for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end();)
	{
		bool findDelMail = false;

		for (std::vector<MailIDType>::iterator idIter = mailIdList.begin(); idIter != mailIdList.end(); ++idIter)
		{
			MailIDType delMailId = (*idIter);
			const MailHead& mailHead = mailIter->mailhead();

			if (mailHead.mailid() == delMailId)
			{
				//check have read
				if (mailHead.status() == MS_READED)
				{
					//check have attachment
					if ((mailIter->gold() > 0 || mailIter->bindgold() > 0 || mailIter->diamond() > 0 ||
						mailIter->binddiamond() > 0 || mailIter->arenascore() || mailHead.attachmentnum() > 0) && !ignoreAttachment)
					{
						//CTODO write to DB log
					}
					else
					{
						findDelMail = true;

						mailIter = mails.erase(mailIter);

						pMail->CacheUpdateMailId(delMailId, UNIT_DELETE);

						deleteMailRsp.add_idlist(delMailId);

						//CTODO write to DB log
					}
				}
			}
		}

		if (findDelMail == false)
		{
			mailIter++;
		}
	}

	g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_DELETEMAIL, &deleteMailRsp);

	return true;
}

bool MailManager::OnGetMailItemListReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWGetMailItemListReq getMailItemListReq;
	WCGetMailItemListRsp getMailItemListRsp;

	if (!getMailItemListReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("CWGetMailItemListReq parse failed...");
		return false;
	}
	ActiveCharacter *pOnline = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnline)
	{
		LogErrFmtPrint("MailManager::OnGetMailItemListReq....nullptr == pOnline , charid:%llu ",charId);
		return false;
	}

	Mail* pMail = FindMail(charId);
	if (nullptr != pMail)
	{
		Mail::MailInfoList& mails = pMail->GetMailData();
		for (uint32_t i = 0; i < mails.size(); i++)
		{
			getMailItemListRsp.add_mailhead()->CopyFrom(mails[i].mailhead());
		}
		Mail::MailInfoList& trade_mails = pMail->GetTradeMailData();
		for (uint32_t j = 0; j < trade_mails.size(); j++)
		{
			getMailItemListRsp.add_mailhead()->CopyFrom(trade_mails[j].mailhead());
		}
	}

	g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_GETMAILITEMLIST, &getMailItemListRsp);

	return true;
}

bool MailManager::OnAskMailReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWAskMailReq askMailReq;
	WCAskMailRsp askMailRsp;

	if (!askMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("CWAskMailReq parse failed...");
		return false;
	}
	ActiveCharacter *pOnline = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnline)
	{
		LogErrFmtPrint("MailManager::OnAskMailReq....nullptr == pOnline , charid:%llu ", charId);
		return false;
	}


	MailIDType mailId = askMailReq.mailid();
	Mail* pMail = FindMail(charId);
	if (nullptr != pMail)
	{
		bool bFind = false;
		Mail::MailInfoList& mails = pMail->GetMailData();
		for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end(); ++mailIter)
		{
			if (mailIter->mailhead().mailid() == mailId)
			{
				bFind = true;
				uint32_t oldStatus = mailIter->mailhead().status();
				//set mail have read
				mailIter->mutable_mailhead()->set_status(MS_READED);
				if (oldStatus == MS_NOT_READ)
				{
					pMail->CacheUpdateMailId(mailId, UNIT_UPDATE);
				}

				askMailRsp.set_mailid(mailId);
				askMailRsp.set_content(mailIter->content());
				askMailRsp.set_gold(mailIter->gold());
				askMailRsp.set_diamond(mailIter->diamond());
				askMailRsp.set_bindgold(mailIter->bindgold());
				askMailRsp.set_binddiamond(mailIter->binddiamond());
				askMailRsp.set_arenascore(mailIter->arenascore());
				askMailRsp.set_selltype(mailIter->selltype());
				askMailRsp.set_sellnum(mailIter->sellnum());
				askMailRsp.mutable_attachmentlist()->CopyFrom(mailIter->attachmentlist());
			}
		}
		if (!bFind)
		{
			Mail::MailInfoList& trade_mails = pMail->GetTradeMailData();
			for (Mail::MailInfoIter mailIter = trade_mails.begin(); mailIter != trade_mails.end(); ++mailIter)
			{
				if (mailIter->mailhead().mailid() == mailId)
				{
					bFind = true;
					uint32_t oldStatus = mailIter->mailhead().status();
					//set mail have read
					mailIter->mutable_mailhead()->set_status(MS_READED);
					if (oldStatus == MS_NOT_READ)
					{
						pMail->CacheUpdateMailId(mailId, UNIT_UPDATE);
					}

					askMailRsp.set_mailid(mailId);
					askMailRsp.set_content(mailIter->content());
					askMailRsp.set_gold(mailIter->gold());
					askMailRsp.set_diamond(mailIter->diamond());
					askMailRsp.set_bindgold(mailIter->bindgold());
					askMailRsp.set_binddiamond(mailIter->binddiamond());
					askMailRsp.set_arenascore(mailIter->arenascore());
					askMailRsp.set_selltype(mailIter->selltype());
					askMailRsp.set_sellnum(mailIter->sellnum());
					askMailRsp.mutable_attachmentlist()->CopyFrom(mailIter->attachmentlist());
				}
			}
		}
	}

	g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_ASKMAIL, &askMailRsp);

	return true;
}

bool MailManager::OnDeleteMailReq(CharIDType charId, RECV_PACKAGE &package)
{
	CWDeleteMailReq deleteMailReq;

	if (!deleteMailReq.ParseFromArray(package.dataAfterHead, package.sizeAfterHead))
	{
		MMOLOG_FMT_ERROR("MailManager::OnDeleteMailReq CWDeleteMailReq parse failed...");
		return false;
	}
	ActiveCharacter *pOnline = g_GetActiveCharacterMgr()->GetOnlineCharacter(charId);
	if (nullptr == pOnline)
	{
		LogErrFmtPrint("MailManager::OnDeleteMailReq....nullptr == pOnline , charid:%llu ", charId);
		return false;
	}

	std::vector<MailIDType> mailIdList;
	mailIdList.clear();
	for (int i = 0; i < deleteMailReq.idlist_size(); i++)
	{
		mailIdList.push_back(deleteMailReq.idlist(i));
	}	
	Mail* pMail = FindMail(charId);
	if (nullptr == pMail)
	{
		LogErrFmtPrint("MailManager::OnDeleteMailReq....nullptr == pMail , charid:%llu ", charId);
		return false;
	}
	WCDeleteMailRsp deleteMailRsp;
	Mail::MailInfoList&	mails = pMail->GetMailData();
	Mail::MailInfoList&	trade_mails = pMail->GetTradeMailData();
	for (std::vector<MailIDType>::iterator idIter = mailIdList.begin(); idIter != mailIdList.end(); ++idIter)
	{
		bool findDelMail = false;
		MailIDType delMailId = (*idIter);
		for (Mail::MailInfoIter mailIter = mails.begin(); mailIter != mails.end();)
		{			
			const MailHead& mailHead = mailIter->mailhead();
			if (mailHead.mailid() == delMailId)
			{
				LogInfoFmtPrint("OnDeleteMailReq...cid:%lu,mailid:%u,topic:%s,content:%s", charId, delMailId, mailHead.topic().c_str(), mailIter->content().c_str());
				findDelMail = true;
				mailIter = mails.erase(mailIter);
				pMail->CacheUpdateMailId(delMailId, UNIT_DELETE);
				deleteMailRsp.add_idlist(delMailId);
			}
			else
			{
				++mailIter;
			}
		}
		if (!findDelMail)
		{
			for (Mail::MailInfoIter mailIterA = trade_mails.begin(); mailIterA != trade_mails.end();)
			{
				const MailHead& mailHead = mailIterA->mailhead();
				if (mailHead.mailid() == delMailId)
				{
					LogInfoFmtPrint("OnDeleteMailReq...trade...cid:%lu,mailid:%u,topic:%s,content:%s", charId, delMailId, mailHead.topic().c_str(), mailIterA->content().c_str());
					findDelMail = true;
					mailIterA = trade_mails.erase(mailIterA);
					pMail->CacheUpdateMailId(delMailId, UNIT_DELETE);
					deleteMailRsp.add_idlist(delMailId);
				}
				else
				{
					++mailIterA;
				}
			}
		}
	}

	g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_DELETEMAIL, &deleteMailRsp);

	return true;
}

Mail* MailManager::FindMail(CharIDType charID)
{
	MailIter iter = _mailMap.find(charID);
	if (iter == _mailMap.end())
	{
		return nullptr;
	}

	return iter->second;
}

Mail* MailManager::MutMail(CharIDType charID)
{
	MailIter iter = _mailMap.find(charID);
	if (iter == _mailMap.end())
	{
		Mail* pMail = _mailPool->MallocObj();
		if (nullptr != pMail)
		{
			pMail->Init(charID);
			_mailMap[charID] = pMail;
			return pMail;
		}
		return nullptr;
	}

	return iter->second;
}

void MailManager::DeleteMail(CharIDType charID)
{
	MailIter iter = _mailMap.find(charID);
	if (iter != _mailMap.end())
	{
		_mailPool->FreeObj(iter->second);
		iter->second = nullptr;
		_mailMap.erase(iter);
	}
}

uint32_t MailManager::GetNewMailId()
{
	_maxMailId = (_maxMailId >= UINT32_MAX) ? 0 : _maxMailId;

	return ++_maxMailId;
}

void MailManager::LoadOfflineMailDataDBRet(AllOfflineMailDBResponse& rsp)
{
	int32_t offset = rsp.offset();
	int32_t size = rsp.info_size();
	bool bFlag = true;
	for (int32_t i = 0; i < size; ++i)
	{
		OfflineMailDBInfo *pInfo = rsp.mutable_info(i);
		if (nullptr != pInfo)
		{
			const OfflineMailList& offlineMailList = pInfo->data();
			CharIDType charId = pInfo->charid();

			Mail *pMail = MutMail(charId);
			if (nullptr == pMail)
			{
				bFlag = false;
				MMOLOG_FMT_ERROR("[center] LoadOfflineMailDataDBRet nullptr == pMail...charid:%lu", charId);
				return;
			}

			pMail->LoadOfflineMailData(offlineMailList);

			_mailMap[charId] = pMail;
		}
		else
		{
			bFlag = false;
			MMOLOG_FMT_ERROR("[center] LoadOfflineMailDataDBRet nullptr == pMail");
			return;
		}
	}
	if (bFlag)
	{
		if (size >= MAX_DATA_COUNT_PER_LOAD)
		{
			LogInfoFmtPrint("LoadOfflineMailDataDBRet...offset:%d, size:%d ", offset, size);
			offset += size;
			AllOfflineMailDBRequest req;
			req.set_offset(offset);
			if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_OFFLINE_MAIL_DATA_REQ, &req))
			{
				MMOLOG_FMT_ERROR("[center] LoadOfflineMailDataDBRet SendDataToDBProxy failed...offset:%d ", offset);
			}
		}
		else
		{
			uint64_t nNowTime = Time::Now().sec();
			uint64_t nInterTime = nNowTime - m_nStartLoadTime;
			MMOLOG_FMT_INFO("[center] LoadOfflineMailDataDBRet finished.... total_sec:%ld,maxMailId:%u  ", nInterTime, _maxMailId);

			//start to load trading mail
			StartLoadTradingMailDataDBRequest();
		}

	}
}

void MailManager::LoadMaxMailIdDBRet(MaxMailIDResponse& rsp)
{
	_maxMailId = rsp.maxmailid();
	m_maxAzDbMailId = rsp.maxzonemailid();

	//加载完最大ID开始加载离线邮件数据
	int32_t offset = 0;
	m_nStartLoadTime = Time::Now().sec();
	AllOfflineMailDBRequest req;
	req.set_offset(offset);
	if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_OFFLINE_MAIL_DATA_REQ, &req))
	{
		MMOLOG_FMT_ERROR("[center] LoadOfflineMailDataDBRet SendDataToDBProxy failed...offset:%d ", offset);
	}
	else
	{
		LogDebugFmtPrint("[center] start load all offline mail data....");
	}
}

void MailManager::StartLoadTradingMailDataDBRequest()
{
	if (g_GetCenterService()->IsConnectDBProxy())
	{
		//start load trading mail
		int32_t offset = 0;
		m_nStartLoadTime = Time::Now().sec();
		AllTradingMailDBRequest req;
		req.set_offset(offset);
		if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_TRADING_MAIL_DATA_REQ, &req))
		{
			MMOLOG_FMT_ERROR("[center] StartLoadTradingMailDataDBRequest SendDataToDBProxy failed...offset:%d ", offset);
		}
		else
		{
			LogDebugFmtPrint("[center] start load all trading mail data....");
		}
	}
	else
	{
		MMOLOG_FMT_ERROR("[center] StartLoadTradingMailDataDBRequest disconnect db...");
	}
}


void MailManager::LoadTradingMailDataDBRet(AllTradingMailDBResponse& rsp)
{
	int32_t offset = rsp.offset();
	int32_t size = rsp.info_size();
	bool bFlag = true;
	for (int32_t i = 0; i < size; ++i)
	{
		TradingMailDBInfo *pInfo = rsp.mutable_info(i);
		if (nullptr != pInfo)
		{
			const MailInfo& mailInfo = pInfo->data();
			const MailHead& mailHead = mailInfo.mailhead();

			CharIDType charID = mailHead.destplayerid();
			Mail *pMail = MutMail(charID);
			if (nullptr == pMail)
			{
				bFlag = false;
				MMOLOG_FMT_ERROR("[center] LoadTradingMailDataDBRet nullptr == pMail...charid:%lu", charID);
				return;
			}

			pMail->LoadTradingMailData(mailInfo);

			_mailMap[charID] = pMail;
		}
		else
		{
			bFlag = false;
			MMOLOG_FMT_ERROR("[center] LoadTradingMailDataDBRet nullptr == pMail");
			return;
		}
	}
	if (bFlag)
	{
		/*if (size >= MAX_DATA_COUNT_PER_LOAD)
		{
			offset += size;
			AllTradingMailDBRequest req;
			req.set_offset(offset);
			req.set_retnum(MAX_DATA_COUNT_PER_LOAD);
			if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_TRADING_MAIL_DATA_REQ, &req))
			{
				MMOLOG_FMT_ERROR("[center] LoadTradingMailDataDBRet SendDataToDBProxy failed...offset:%d ", offset);
			}
		}
		else
		{
			uint64_t nNowTime = Time::Now().sec();
			uint64_t nInterTime = nNowTime - m_nStartLoadTime;
			m_bFinishLoad = true;
			MMOLOG_FMT_INFO("[center] LoadTradingMailDataDBRet finished.... total_sec:%lld  ", nInterTime);

			//start all trading mail timer
			StartAllTradingMailTimer();
		}*/

		if (size > 0)
		{
			LogInfoFmtPrint("LoadTradingMailDataDBRet...offset:%d, size:%d ", offset, size);
			offset += size;
			AllTradingMailDBRequest req;
			req.set_offset(offset);
			if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_TRADING_MAIL_DATA_REQ, &req))
			{
				MMOLOG_FMT_ERROR("[center] LoadTradingMailDataDBRet SendDataToDBProxy failed...offset:%d ", offset);
			}
		}
		else
		{
			uint64_t nNowTime = Time::Now().sec();
			uint64_t nInterTime = nNowTime - m_nStartLoadTime;
			m_bFinishLoad = true;
			MMOLOG_FMT_INFO("[center] LoadTradingMailDataDBRet finished.... total_sec:%lld  ", nInterTime);

			//start all trading mail timer
			StartAllTradingMailTimer();
		}

	}
}

void MailManager::LoadOnlineCharacterMailDataDBRet(CharacterMailDBResponse& rsp)
{
	CharIDType charId = rsp.charid();
	Mail* pMail = MutMail(charId);
	if (nullptr != pMail)
	{
		pMail->OnLogin(rsp);
		//如果有未读邮件 通知客户端
		if (pMail->IsHaveNoRead())
		{
			WCUnReadMailNotify notify;
			notify.set_hasnew(true);
			g_GetCenterService()->SendDataToClient(charId, EMODULE_ID_MAIL, SERVER_TO_CLIENT_UNREADMAILNOTIFY, &notify);
		}
	}
	else
	{
		MMOLOG_FMT_ERROR("[center] CharacterMailDBResponse return pMail is nullptr charId:%lu", charId);
	}

}

void MailManager::StartAllTradingMailTimer()
{
	MailIter iter = _mailMap.begin();
	for (; iter != _mailMap.end(); ++iter)
	{
		if (nullptr != iter->second)
		{
			iter->second->InitTradingMail();
		}
	}
}

void MailManager::CalcuMaxMailID(MailIDType mailId)
{
	if (mailId > _maxMailId)
	{
		_maxMailId = mailId;
	}
}

void MailManager::SendSystemTextMail(CharIDType destPlayerId, std::string topical, std::string content)
{
	OfflineCharacterData* pDestOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(destPlayerId);
	if (nullptr == pDestOfflineCharacterData)
	{
		LogErrFmtPrint("MailManager::SendSystemTextMail....nullptr == pDestOfflineCharacterData...cid:%lu, topic:%s, content:%s ", destPlayerId, topical.c_str(), content.c_str());
		return;
	}

	MailInfo mailInfo;
	MailHead* mailHead = mailInfo.mutable_mailhead();
	mailHead->set_type(MAIL_TYPE_SYSTEM);
	mailHead->set_sendplayerid(0);
	string strName = "系统";
	mailHead->set_sendplayername(strName);
	mailHead->set_destplayerid(destPlayerId);
	mailHead->set_topic(topical);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(0);
	mailInfo.set_content(content);

	//发送系统邮件
	int32_t ret = SendMail(mailInfo);
	if (RET_SUCCESS != ret)
	{
		LogErrFmtPrint("SendSystemTextMail...SendMail failed...destPlayerId:%lu, ret:%d,topic:%s,content:%s ", destPlayerId, ret, topical.c_str(), content.c_str());
	}
}

bool MailManager::SendSystemMail(CharIDType dstCharId, string &strTopic, string &strContent, LIST_ITEM &lstItem, uint32_t gold, uint32_t bindGold, uint32_t diamond, uint32_t bindDiamond, uint32_t arenaScore, uint32_t source /* = MAIL_SOURCE_NONE */)
{
	OfflineCharacterData* pDestOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(dstCharId);
	if (nullptr == pDestOfflineCharacterData)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail...nullptr == pDestOfflineCharacterData...dstCharId:%lu,content:%s,strTopic:%s,source:%d ", dstCharId, strContent.c_str(), strTopic.c_str(), source);
		return false; 
	}
	//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
	if (nullptr != pRecvOpenLevel)
	{
		if (pDestOfflineCharacterData->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			LogErrFmtPrint("[center] MailManager::SendSystemMail...recv mail level not enough...dstCharId:%lu,content:%s,strTopic:%s,source:%d ", dstCharId, strContent.c_str(), strTopic.c_str(), source);
			return false;
		}
	}

	MailInfo mailInfo;
	MailHead* mailHead = mailInfo.mutable_mailhead();
	mailHead->set_type(MAIL_TYPE_SYSTEM);
	mailHead->set_sendplayerid(0);
	string strName = "系统";
	mailHead->set_sendplayername(strName);
	mailHead->set_destplayerid(dstCharId);
	mailHead->set_topic(strTopic);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(0);
	mailInfo.set_content(strContent);
	mailInfo.set_gold(gold);
	mailInfo.set_bindgold(bindGold);
	mailInfo.set_diamond(diamond);
	mailInfo.set_binddiamond(bindDiamond);
	mailInfo.set_arenascore(arenaScore);
	mailInfo.set_source(source);

	MailAttachmentList* pMailAttachmentList = mailInfo.mutable_attachmentlist();
	if (nullptr == pMailAttachmentList)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail...nullptr == pMailAttachmentList...dstCharId:%lu,strTopic:%s,source:%d ", dstCharId, strTopic.c_str(), source);
		return false;
	}
	//附件物品生成处理
	if (lstItem.size() > 0)
	{
		VEC_ITEM_PROTO_EX vecProtoEx;
		vecProtoEx.clear();
		SItemCond itemCond;
		g_GetOfflineCharacterDataMgr()->GetItemCond(dstCharId, itemCond);
		if (!g_GetItemMgr()->CreateItem(lstItem, vecProtoEx, itemCond))
		{
			LogErrFmtPrint("[center] MailManager::SendSystemMail...g_GetItemMgr()->CreateItem...dstCharId:%lu,content:%s,strTopic:%s,source:%d ", dstCharId, strContent.c_str(), strTopic.c_str(), source);
			return false;
		}		
		VEC_ITEM_PROTO_EX::iterator iterItem = vecProtoEx.begin();
		for (; iterItem != vecProtoEx.end(); ++iterItem)
		{
			const ItemProtoInfo protoInfo = (*iterItem);
			ItemProtoInfo *proto = pMailAttachmentList->add_iteminfo();
			if (nullptr == proto)
			{
				LogErrFmtPrint("[center] MailManager::SendSystemMail...nullptr == proto...dstCharId:%lu, itemid:%u, num:%d ,strTopic:%s,source:%d", dstCharId, protoInfo.item_id(), protoInfo.item_num(), strTopic.c_str(), source);
				continue;
			}
			proto->CopyFrom(protoInfo);
		}
		mailHead->set_attachmentnum(pMailAttachmentList->iteminfo_size());
	}

	//发送系统邮件
	int32_t ret = SendMail(mailInfo);
	if (RET_SUCCESS != ret)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail...SendMail failed...ret:%d,dstCharId:%lu,gold:%d,diamond:%d,bindDiamond:%d,strContent:%s, strTopic:%s,source:%d ", ret, dstCharId, gold, diamond, bindDiamond, strContent.c_str(), strTopic.c_str(), source);
		return false;
	}
	
	return true;
}

bool MailManager::SendSystemMail(CharIDType dstCharId, string strTopic, string strContent, uint32_t gold, uint32_t bindGold, uint32_t diamond, uint32_t bindDiamond, uint32_t arenaScore, VEC_ITEM_PROTO_EX& vecProtoEx, uint32_t source)
{
	OfflineCharacterData* pDestOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(dstCharId);
	if (nullptr == pDestOfflineCharacterData)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail 11...nullptr == pDestOfflineCharacterData...destCharId:%lu,gold:%d,diamond:%d,bindDiamond:%d,content:%s, strTopic:%s,source:%d ", dstCharId, gold, diamond, bindDiamond, strContent.c_str(), strTopic.c_str(), source);
		return false;
	}

	//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
	if (nullptr != pRecvOpenLevel)
	{
		if (pDestOfflineCharacterData->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			LogErrFmtPrint("[center] MailManager::SendSystemMail 11...recv mail level not enough...dstCharId:%lu,content:%s,strTopic:%s,source:%d ", dstCharId, strContent.c_str(), strTopic.c_str(), source);
			return false;
		}
	}

	MailInfo mailInfo;
	MailHead* mailHead = mailInfo.mutable_mailhead();
	mailHead->set_type(MAIL_TYPE_SYSTEM);
	mailHead->set_sendplayerid(0);
	string strName = "系统";
	mailHead->set_sendplayername(strName);
	mailHead->set_destplayerid(dstCharId);
	mailHead->set_topic(strTopic);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(0);
	mailInfo.set_content(strContent);
	mailInfo.set_gold(gold);
	mailInfo.set_bindgold(bindGold);
	mailInfo.set_diamond(diamond);
	mailInfo.set_binddiamond(bindDiamond);
	mailInfo.set_arenascore(arenaScore);
	mailInfo.set_source(source);
	MailAttachmentList* pMailAttachmentList = mailInfo.mutable_attachmentlist();
	if (nullptr == pMailAttachmentList)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail 11...nullptr == pMailAttachmentList...dstCharId:%lu, strTopic:%s,source:%d ", dstCharId, strTopic.c_str(), source);
		return false;
	}
	if (vecProtoEx.size() > 0)
	{
		VEC_ITEM_PROTO_EX::iterator iterItem = vecProtoEx.begin();
		for (; iterItem != vecProtoEx.end(); ++iterItem)
		{
			const ItemProtoInfo protoInfo = (*iterItem);
			ItemProtoInfo *proto = pMailAttachmentList->add_iteminfo();
			if (nullptr != proto)
			{
				proto->CopyFrom(protoInfo);
			}
		}
		mailHead->set_attachmentnum(pMailAttachmentList->iteminfo_size());
	}

	//发送系统邮件
	int32_t ret = SendMail(mailInfo);
	if(RET_SUCCESS != ret)
	{
		LogErrFmtPrint("[center] MailManager::SendSystemMail 11... SendMail failed...ret:%d,dstCharId:%lu,gold:%d,diamond:%d,bindDiamond:%d,strContent:%s,strTopic:%s, source:%d", ret, dstCharId, gold, diamond, bindDiamond, strContent.c_str(), strTopic.c_str(), source);
		return false;
	}
	return true;
}

bool MailManager::SendP2PMail(CharIDType dstCharId, CharIDType sendCharId, string fromName, string strTopic, string strContent, LIST_ITEM &lstItem, uint32_t gold, uint32_t bindGold, uint32_t diamond, uint32_t bindDiamond, uint32_t arenaScore, uint32_t source)
{
	OfflineCharacterData* pDestOfflineCharacterData = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(dstCharId);
	if (nullptr == pDestOfflineCharacterData)
	{
		LogErrFmtPrint("[center] MailManager::SendP2PMail ...nullptr == pDestOfflineCharacterData...destCharId:%lu ", dstCharId);
		return false;
	}

	//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
	const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
	if (nullptr != pRecvOpenLevel)
	{
		if (pDestOfflineCharacterData->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			LogErrFmtPrint("[center] MailManager::SendP2PMail...recv mail level not enough...dstCharId:%lu,content:%s,strTopic:%s,source:%d ", dstCharId, strContent.c_str(), strTopic.c_str(), source);
			return false;
		}
	}

	MailInfo mailInfo;
	MailHead* mailHead = mailInfo.mutable_mailhead();
	mailHead->set_type(MAIL_TYPE_SYSTEM);
	mailHead->set_sendplayerid(sendCharId);
	mailHead->set_sendplayername(fromName);
	mailHead->set_destplayerid(dstCharId);
	mailHead->set_topic(strTopic);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(0);
	mailInfo.set_content(strContent);
	mailInfo.set_gold(gold);
	mailInfo.set_bindgold(bindGold);
	mailInfo.set_diamond(diamond);
	mailInfo.set_binddiamond(bindDiamond);
	mailInfo.set_arenascore(arenaScore);
	mailInfo.set_source(source);
	MailAttachmentList* pMailAttachmentList = mailInfo.mutable_attachmentlist();
	if (nullptr == pMailAttachmentList)
	{
		LogErrFmtPrint("[center] MailManager::SendP2PMail ...nullptr == pMailAttachmentList...dstCharId:%lu,strTopic:%s ", dstCharId, strTopic.c_str());
		return false;
	}

	//附件物品生成处理
	if (lstItem.size() > 0)
	{
		VEC_ITEM_PROTO_EX vecProtoEx;
		vecProtoEx.clear();
		SItemCond itemCond;
		g_GetOfflineCharacterDataMgr()->GetItemCond(dstCharId, itemCond);
		if (!g_GetItemMgr()->CreateItem(lstItem, vecProtoEx, itemCond))
		{
			LogErrFmtPrint("[center] MailManager::SendP2PMail...g_GetItemMgr()->CreateItem...dstCharId:%lu,content:%s ", dstCharId, strContent.c_str());
			return false;
		}
		VEC_ITEM_PROTO_EX::iterator iterItem = vecProtoEx.begin();
		for (; iterItem != vecProtoEx.end(); ++iterItem)
		{
			const ItemProtoInfo protoInfo = (*iterItem);
			ItemProtoInfo *proto = pMailAttachmentList->add_iteminfo();
			if (nullptr == proto)
			{
				LogErrFmtPrint("[center] MailManager::SendP2PMail...nullptr == proto...dstCharId:%lu, itemid:%u, num:%d ", dstCharId, protoInfo.item_id(), protoInfo.item_num());
				continue;
			}
			proto->CopyFrom(protoInfo);
		}
		mailHead->set_attachmentnum(pMailAttachmentList->iteminfo_size());
	}

	int32_t ret = SendMail(mailInfo);
	if (RET_SUCCESS != ret)
	{
		LogErrFmtPrint("[center] MailManager::SendP2PMail... SendMail failed...ret:%d,dstCharId:%lu,gold:%d,diamond:%d,bindDiamond:%d,strContent:%s,strtopic:%s", ret, dstCharId, gold, diamond, bindDiamond, strContent.c_str(),strTopic.c_str());
		return false;
	}
	return true;
}

void MailManager::SendZoneMail(string &strTopic, string &strContent, LIST_ITEM &lstItem, uint32_t gold,uint32_t bindGold, uint32_t diamond, uint32_t bindDiamond, uint32_t arenaScore, uint32_t source /* = MAIL_SOURCE_NONE */)
{
	MailInfo  curZoneMail;
	MailHead* mailHead = curZoneMail.mutable_mailhead();
	mailHead->set_type(MAIL_TYPE_SYSTEM);
	mailHead->set_sendplayerid(0);
	string strName = "系统";
	mailHead->set_sendplayername(strName);
	//mailHead->set_destplayerid(dstCharId);
	mailHead->set_topic(strTopic);
	mailHead->set_createtime(Time::Now().sec());
	mailHead->set_status(MS_NOT_READ);
	mailHead->set_fetch(0);
	mailHead->set_attachmentnum(0);
	curZoneMail.set_content(strContent);
	curZoneMail.set_gold(gold);
	curZoneMail.set_bindgold(bindGold);
	curZoneMail.set_diamond(diamond);
	curZoneMail.set_binddiamond(bindDiamond);
	curZoneMail.set_arenascore(arenaScore);
	curZoneMail.set_source(source);
	MailAttachmentList* pMailAttachmentList = curZoneMail.mutable_attachmentlist();
	if (nullptr == pMailAttachmentList)
	{
		LogErrFmtPrint("[center] MailManager::SendZoneMail 11...nullptr == pMailAttachmentList");
		return;
	} 
	//附件物品生成处理
	if (lstItem.size() > 0)
	{
		VEC_ITEM_PROTO_EX vecProtoEx;
		vecProtoEx.clear();
		SItemCond itemCond;//因为是全部邮件，所以这里无法获取某一个玩家的物品条件，只能用默认值
		if (!g_GetItemMgr()->CreateItem(lstItem, vecProtoEx, itemCond))
		{
			LogErrPrint("[center] MailManager::SendSystemMail...g_GetItemMgr()->CreateItem...");
			return;
		}		
		VEC_ITEM_PROTO_EX::iterator iterItem = vecProtoEx.begin();
		for (; iterItem != vecProtoEx.end(); ++iterItem)
		{
			const ItemProtoInfo protoInfo = (*iterItem);
			ItemProtoInfo *proto = pMailAttachmentList->add_iteminfo();
			if (nullptr == proto)
			{
				LogErrFmtPrint("[center] MailManager::SendZoneMail...nullptr == proto.., itemid:%u, num:%d ", protoInfo.item_id(),protoInfo.item_num());
				continue;
			}
			proto->CopyFrom(protoInfo);
		}
		mailHead->set_attachmentnum(pMailAttachmentList->iteminfo_size());
	}

	if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_ALL_ZONE_MAIL_SAVE_REQ, &curZoneMail))
		LogErrFmtPrint("SendZoneMail send db fail %s", strTopic.c_str());
	g_GetTimerAxis()->SetTimer(TIMER_ID_ZONEMAIL_SAVE_WAIT_TIME, 120000, this, 1);

	LogInfoFmtPrint("recv zone mail from web topic:%s", strTopic.c_str());
}

bool MailManager::ZoneMailSaveHandleRsp(RECV_PACKAGE& package)
{
    bool result = false;
    bool retCode = false;
	ZoneMailSaveRsp zmSRsp;

	retCode = zmSRsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
	MMOLOG_PROCESS_ERROR(retCode);

	m_maxAzDbMailId = zmSRsp.mailid();
	g_GetCenterAccountMgr()->GetOnlineCidQueue(m_curSendAllZoneQueue);

	if (!m_curSendAllZoneQueue.empty())
		g_GetTimerAxis()->SetTimer(TIMER_ID_ZONE_MAIL_ONLINE_HADLE, 5000, this);

	g_GetTimerAxis()->KillTimer(TIMER_ID_ZONEMAIL_SAVE_WAIT_TIME, this);

	LogInfoFmtPrint("recv ZoneMailSaveHandleRsp zoneMailId:%d", m_maxAzDbMailId);
    result = true;
Exit0:
	return result;
}

void MailManager::ZoneMailHandle()
{
	uint32_t count = 0;
	CharIDType cid = 0;
	Mail *pMail = nullptr;
	OfflineCharacterData *pOffline = nullptr;

	LogInfoFmtPrint("ZoneMailHandle send zonemail to characters zonemailId:%d, %d", m_maxAzDbMailId, m_curSendAllZoneQueue.size());
	while (!m_curSendAllZoneQueue.empty() && count < 200)
	{
		cid = m_curSendAllZoneQueue.front();
		pOffline = g_GetOfflineCharacterDataMgr()->GetOfflineCharacter(cid);
		pMail = FindMail(cid);
		m_curSendAllZoneQueue.pop();
		count++;
		if (pMail == nullptr || pMail->GetZoneMailId() >= m_maxAzDbMailId || nullptr == pOffline)
			continue;
		//对方收信开启等级 FunctionUnlock_ID_TYPE_RECV_MAIL
		const FunctionunlockFunctionUnlockCfgInfo *pRecvOpenLevel = g_GetFunctionunlockFunctionUnlockCfgTable()->GetFunctionunlockFunctionUnlockCfgInfo(FunctionUnlock_ID_TYPE_RECV_MAIL);
		if (nullptr != pRecvOpenLevel && pOffline->GetLevel() < pRecvOpenLevel->unlockLevel)
		{
			continue;
		}

		TransZoneMailToCharMail(cid, pMail->GetZoneMailId());
	}

	if (m_curSendAllZoneQueue.empty())
		g_GetTimerAxis()->KillTimer(TIMER_ID_ZONE_MAIL_ONLINE_HADLE, this);
}

//清理所有等待逻辑服返回的邮件 type: 1 玩家下线(typeVal:玩家cid)，2 逻辑服崩溃(typeVal:逻辑服ID)
void MailManager::ClearAllWaitRetMail(int8_t type, uint64_t typeVal)
{
	if (1 != type && 2 != type)
	{
		return;
	}
	MAP_UINT64_SET_UINT32 mapCharMail;
	mapCharMail.clear();
	if (1 == typeVal)
	{
		//玩家下线，领取附件等待逻辑服返回结果的，此时 中心服不知道逻辑服领取成功失败，这里默认当作成功处理
		CharIDType cid = typeVal;
		PlayerExtractStateMap::iterator iterState = _extractInfo.find(cid);
		if (iterState == _extractInfo.end())
		{
			return;
		}
		MailExtractStateMap &stateMap = iterState->second;
		if (stateMap.size() <= 0)
		{
			return;
		}
		//
		SET_UINT32 setMail;
		setMail.clear();		
		MailExtractStateMap::iterator iterMail = stateMap.begin();
		for (; iterMail != stateMap.end(); ++iterMail)
		{
			setMail.insert(iterMail->first);
		}
		mapCharMail[cid] = setMail;
	}
	else
	{
		//逻辑服崩溃，逻辑服领取附件返回失败，这时候需要清除领取附件标记，
		//但是中心服不知道逻辑服领取成功失败，这里默认当作成功处理
		uint32_t logicId = (uint32_t)typeVal;
		PlayerExtractStateMap::iterator iterState = _extractInfo.begin();
		for (; iterState != _extractInfo.end(); ++iterState)
		{
			CharIDType charId = iterState->first;
			MailExtractStateMap &stateMap = iterState->second;
			MailExtractStateMap::iterator iterMail = stateMap.begin();
			for (; iterMail != stateMap.end(); ++iterMail)
			{
				if (logicId != iterMail->second.logicId)
				{
					continue;
				}
				uint32_t mailId = iterMail->first;
				MAP_UINT64_SET_UINT32::iterator iterChar = mapCharMail.find(charId);
				if (iterChar != mapCharMail.end())
				{
					iterChar->second.insert(mailId);
				}
				else
				{
					SET_UINT32 setMail;
					setMail.clear();
					setMail.insert(mailId);
					mapCharMail[charId] = setMail;
				}
			}
		}
	}
	//
	MAP_UINT64_SET_UINT32::iterator iterUpdate = mapCharMail.begin();
	for (; iterUpdate != mapCharMail.end(); ++iterUpdate)
	{
		SET_UINT32 &setMail = iterUpdate->second;
		CharIDType charId = iterUpdate->first;
		GWUpdateMailNotify notify;
		notify.set_charid(charId);
		notify.set_ret(RET_SUCCESS);
		SET_UINT32::iterator iterId = setMail.begin();
		for (; iterId != setMail.end(); ++iterId)
		{
			uint32_t mailId = (*iterId);
			LogInfoFmtPrint("[center] MailManager::ClearAllWaitRetMail...type:%d,typeval:%lu,charId:%lu mailid:%u ",type,typeVal, charId, mailId);
			notify.add_idlist(mailId);
		}
		OnUpdateMailNotify(notify);
	}
}

void MailManager::TransZoneMailToCharMail(CharIDType cid, uint32_t oldZoneMailId)
{
	if (m_maxAzDbMailId == oldZoneMailId)
		return;

	ZoneMailTransCharacterMailReq zmtcReq;
	zmtcReq.set_cid(cid);
	zmtcReq.set_mailid(oldZoneMailId);

	if (!g_GetCenterService()->SendDataToDBProxy(EMODULE_ID_MAIL, DBPROXY_PROTOCOL_ZONE_MAIL_TRANS_REQ, &zmtcReq))
		LogErrPrint("TransZoneMailToCharMail, send db fail");
}

bool MailManager::TransZoneMailToCharMailRsp(RECV_PACKAGE& package)
{
    bool result = false;
    bool retCode = false;
	Mail *pMail = nullptr;
	uint32_t maxId = 0;
	uint32_t curId = 0;
	ZoneMailTransCharacterMailRsp zmtcRsp;
	MailHead* pMailHead = nullptr;

	retCode = zmtcRsp.ParseFromArray(package.dataAfterHead, package.sizeAfterHead);
	MMOLOG_PROCESS_ERROR(retCode);

	pMail = FindMail(zmtcRsp.cid());
	if (pMail == nullptr)
	{
		LogWarningFmtPrint("TransZoneMailToCharMailRsp, cannt find cid mails, cid:%lu", zmtcRsp.cid());
		return false;
	}

	for (int i = 0; i < zmtcRsp.data_size(); i++)
	{
		const MailInfo constMail = zmtcRsp.data(i);
		curId = constMail.mailhead().mailid();
		if (curId <= pMail->GetZoneMailId())
			continue;

		maxId = curId > maxId ? curId : maxId;
		MailInfo mail(constMail);
		pMailHead = mail.mutable_mailhead();
		if (pMailHead == nullptr)
			continue;;
		pMailHead->set_destplayerid(zmtcRsp.cid());
		int32_t ret = SendMail(mail);
		if (RET_SUCCESS != ret)
		{
			LogErrFmtPrint("TransZoneMailToCharMailRsp...cid:%lu, topic:%s,content:%s ", zmtcRsp.cid(), pMailHead->topic().c_str(), mail.content().c_str());
		}
	}
	if (maxId > 0)
		pMail->SetZoneMailId(maxId);

    result = true;
Exit0:
	return result;
}
